package com.yubest.demo.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import com.yubest.demo.exception.MyException;

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @Author hweiyu
 * @Description
 * @Date 2021/7/6 11:56
 */
public class SignUtil {

    //时间戳
    private static final String TIMESTAMP_KEY = "timeStamp";

    //随机字符串
    private static final String RAND_KEY = "randStr";

    //签名值
    private static final String SIGN_KEY = "sign";

    //过期时间，15分钟
    private static final Long EXPIRE_TIME = 15 * 60L;

    /**
     * 签名方法
     * 1.加入时间戳和随机字符串参数
     * 2.所有key按字典序排序
     * 3.如果value是对象或数组，转换成json字符串
     * 4.过滤掉所有value为空的字段
     * 5.将key和value进行拼接，最后加上密钥key，规则：key1=value1&key2=value2 ... &key=xxx
     * 6.将4得到的字符串进行MD5加密，然后转换成大写，最终生成即为sign的值
     *
     * 例如：
     * {
     *     "k3": {
     *         "k4": "v4",
     *         "k5": "v5"
     *     },
     *     "k6": [
     *         {
     *             "k7": "v7",
     *             "k8": 8
     *         }
     *     ],
     *     "k9": 9,
     *     "k2": "v2",
     *     "k1": "v1"
     * }
     *
     * 拼接字符示例：k1=v1&k2=v2&k3={"k4":"v4","k5":"v5"}&k6=[{"k7":"v7","k8":8}]&k9=9&randStr=0.8244844229922232&timeStamp=162555624011&key=xxx
     *
     * @param map
     * @param key
     * @return
     */
    public static String sign(TreeMap<String, String> map, String key) {
        if (!map.containsKey(TIMESTAMP_KEY)) {
            map.put(TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis() / 1000));
        }
        if (!map.containsKey(RAND_KEY)) {
            map.put(RAND_KEY, String.valueOf(ThreadLocalRandom.current().nextDouble()));
        }
        StringBuilder buf = new StringBuilder();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (!SIGN_KEY.equals(entry.getKey()) && StrUtil.isNotBlank(entry.getValue())) {
                buf.append("&").append(entry.getKey()).append("=").append(entry.getValue());
            }
        }
        String preSign = buf.substring(1) + "&key=" + key;
        String sign = MD5.create().digestHex(preSign).toUpperCase();
        if (!map.containsKey(SIGN_KEY)) {
            map.put(SIGN_KEY, sign);
        }
        return sign;
    }

    public static void verify(TreeMap<String, String> map, String key) {
        if (StrUtil.isBlank(map.get(TIMESTAMP_KEY))
                || StrUtil.isBlank(map.get(RAND_KEY))
                || StrUtil.isBlank(map.get(SIGN_KEY))) {
            throw new MyException("必填参数为空");
        }
        long timeStamp = Long.valueOf(map.get(TIMESTAMP_KEY));
        long expireTime = timeStamp + EXPIRE_TIME;
        if (System.currentTimeMillis() / 1000 > expireTime) {
            throw new MyException("请求已过期");
        }
        String sign = sign(map, key);
        if (!Objects.equals(sign, map.get(SIGN_KEY))) {
            throw new MyException("签名错误");
        }
    }

}
